// const Promise = require('./myPromise');

// 判断某个变量是否为一个 promise
const isPromise = (value) => {
  if ((typeof value === 'object' && value !== null) || typeof value === 'function') {
    let then = value.then;
    if (typeof then === 'function') return true;
  }
  return false;
}

/**
 * Promise.all 是类方法，只能通过 Promise.all 调用，返回一个 Promise 实例
 * 1) 如果传入参数为 Promise，且所有 Promise 都变成完成状态或者入参是普通值，就会把成功的结果/普通值放到数组，Promise.all 返回的 promise 异步地变为完成，结果就是这个数组
 * 2) 只要有一个 Promise 是返回失败，Promise.all 异步地将失败的那个结果给失败状态的回调函数，结果为失败的原因
 */
Promise.all = function (args) {
  // 返回一个新的 Promise
  return new Promise((resolve, reject) => {
    // 0.如果没传参，抛异常
    if (!Array.isArray(args)) {
      throw new TypeError(`${args} is not iterable (cannot read property Symbol...)`);
    }
    // 1.获取传参数组的长度
    let len = args.length >>> 0;
    // 2.1定义存放返回结果的数组
    let result = [];
    // 传参数组长度为 0 时需要直接 resolve([])
    if (!len) return resolve(result);
    // 2.2定义异步执行回调的计数器，目的是解决多个异步并发问题
    let index = 0;

    // 定义设置返回结果的函数
    function processData(key, value) {
      result[key] = value;
      index++;
      // 判断计数器和传参数组长度是否相等，相等表示遍历完了数组，把 result 作为成功回调的值
      if (index === len) {
        resolve(result);
      }
    }
    for (let i = 0; i < len; i++) {
      let current = args[i];
      // 3.1根据当前项是否为 Promise，是就执行 then 方法，把成功回调的结果存入数组
      if (isPromise(current)) {
        current.then(data => {
          processData(i, data);
          // 失败的时候直接调用对应的失败回调
        }, reject);
      } else {
        // 3.2当前项为普通值，直接设置对应的返回结果数组项为当前项
        processData(i, current);
      }
    }
  })
}

// 测试 Promise.all
const fs = require('fs')

function read(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err);
      resolve(data);
    })
  })
}

// 测试案例一：正常输出
// let arr1 = [1, '我', false, read('./file/b.txt'), 999]
// Promise.all(arr1).then(res => {
//   console.log(res);
// })

// 测试案例二：如果 resolve(p) 此时应该会提示循环引用
let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve('gg')
    reject('出错了')
    // resolve(p)
  }, 5000)
})
let arr2 = [1, '我', read('./file/b.txt'), 999, p]
Promise.all(arr2).then(res => {
  console.log(res);
}, err => {
  console.log('err：' + err)
})

// 测试案例三：传入空数组
Promise.all([]).then(res => {
  console.log(res);
})

// 测试案例四：传参不是数组类型
Promise.all(5).then(res => {
  console.log(res)
}, err => {
  console.log('err：' + err)
})
